package com.avecsys.quotemanager.dao.common;

import static org.hibernate.criterion.Example.create;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Expression;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException;

import com.avecsys.quotemanager.exception.DAOException;
import com.avecsys.quotemanager.pojos.BasePojo;
import com.avecsys.quotemanager.utils.TransactionStatus;

public class BaseDAOImpl extends HibernateTemplateManagerDAO implements BaseDAO
{

	private static final Log log = LogFactory.getLog(BaseDAOImpl.class);

	@Override
	public TransactionStatus save(final BasePojo persistantPojo) throws DAOException
	{
		TransactionStatus transactionStatus = new TransactionStatus(
				EnumTransactionStatus.SAVE_SUCESS.ordinal());
		try
		{
			getHibernateTemplate().execute(new HibernateCallback<Object>()
			{
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException
				{
					session.save(persistantPojo);
					return persistantPojo;
				}
			});
		}
		catch (Exception e)
		{
			log.error(e);
			transactionStatus.setStatus(EnumTransactionStatus.SAVE_FAIL.ordinal());
			throw new DAOException(e);
		}
		return transactionStatus;
	}

	@Override
	public TransactionStatus update(final BasePojo persistantPojo) throws DAOException
	{
		TransactionStatus transactionStatus = new TransactionStatus(
				EnumTransactionStatus.UPDATE_SUCCESS.ordinal());
		try
		{
			getHibernateTemplate().execute(new HibernateCallback<Object>()
			{
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException
				{
					session.update(persistantPojo);
					return persistantPojo;
				}
			});
		}
		catch (HibernateOptimisticLockingFailureException holfe)
		{
			log.error(holfe);
			transactionStatus.setStatus(EnumTransactionStatus.OPTIMIST_LOCK_FAIL.ordinal());
		}
		catch (Exception e)
		{
			e.printStackTrace();
			log.error(e);
			transactionStatus.setStatus(EnumTransactionStatus.UPDATE_FAIL.ordinal());
			throw new DAOException(e);
		}
		return transactionStatus;
	}

	@Override
	public List findAll(final BasePojo basePojo) throws DAOException
	{
		try
		{
			return getHibernateTemplate().executeFind(new HibernateCallback<List<BasePojo>>()
			{
				@Override
				public List<BasePojo> doInHibernate(final Session session) throws HibernateException,
						SQLException
				{
					@SuppressWarnings("unchecked")
					final List<BasePojo> results = (List<BasePojo>) session.createCriteria(
							basePojo.getClass())
							.add(create(basePojo))
							.list();
					log.debug("findAll successful, result size: " + results.size());
					return results;
				}
			});
		}
		catch (Exception e)
		{
			log.error("find by example failed", e);
			throw new DAOException(e);
		}
	}

	/**
	 * Method to get any SubClass of BasePojo instance by Pk
	 * 
	 * @param BasePojo
	 * @param Pk
	 * @return BasePojo
	 * @throws DAOException
	 */
	@Override
	public BasePojo findById(final BasePojo instance, final Long pk) throws DAOException
	{
		try
		{
			return (BasePojo) getHibernateTemplate().execute(new HibernateCallback<BasePojo>()
			{
				@Override
				public BasePojo doInHibernate(Session session) throws HibernateException, SQLException
				{
					return (BasePojo) session.get(instance.getClass(), pk);
				}
			});
		}
		catch (Exception e)
		{
			log.error("find by id failed", e);
			throw new DAOException(e);
		}
	}

	/**
	 * Method to get any SubClass of BasePojo instance with status 'A' by Pk
	 * 
	 * @param BasePojo
	 * @param Pk
	 * @return BasePojo
	 * @throws DAOException
	 */
	@Override
	public BasePojo findActiveById(final BasePojo basePojo, final Long pk) throws DAOException
	{
		try
		{
			return (BasePojo) getHibernateTemplate().execute(new HibernateCallback<BasePojo>()
			{
				@Override
				public BasePojo doInHibernate(Session session) throws HibernateException, SQLException
				{
					Criteria criteria = session.createCriteria(basePojo.getClass());
					criteria.add(Expression.eq("status", EnumDBRecordStatus.ACTIVE.status()));
					criteria.add(Expression.eq(basePojo.getPkColumnName(), pk));
					List<BasePojo> result = criteria.list();
					if (result.size() > 0)
					{
						return (BasePojo) result.get(0);
					}
					else
					{
						return null;
					}
				}
			});
		}
		catch (Exception e)
		{
			log.error("findActive by id failed", e);
			throw new DAOException(e);
		}
	}

	/**
	 * Method to delete the persisted object
	 * 
	 * @param instance
	 * @return An Integer Status code value
	 * @throws DAOException
	 */
	@Override
	public TransactionStatus delete(final BasePojo instance) throws DAOException
	{
		TransactionStatus transactionStatus = new TransactionStatus(
				EnumTransactionStatus.DELETE_SUCCESS.ordinal());
		try
		{
			getHibernateTemplate().execute(new HibernateCallback<BasePojo>()
			{
				@Override
				public BasePojo doInHibernate(Session session) throws HibernateException, SQLException
				{
					session.delete(instance);
					return instance;
				}
			});
		}
		catch (Exception e)
		{
			log.error(e);
			transactionStatus.setStatus(EnumTransactionStatus.DELETE_FAIL.ordinal());
			throw new DAOException(e);
		}
		return transactionStatus;
	}

	/**
	 * Method to mark the record as delete with status 'D'
	 * 
	 * @param instance
	 * @return TransactionStatus
	 * @throws DAOException
	 */
	@Override
	public TransactionStatus markAsDeleted(final BasePojo basePojo) throws DAOException
	{
		TransactionStatus transactionStatus = new TransactionStatus(
				EnumTransactionStatus.DELETE_SUCCESS.ordinal());
		try
		{
			getHibernateTemplate().execute(new HibernateCallback<Object>()
			{
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException
				{
					basePojo.setStatus(EnumDBRecordStatus.DELETED.status());
					session.update(basePojo);
					return basePojo;
				}
			});
		}
		catch (Exception e)
		{
			log.error(e);
			transactionStatus.setStatus(EnumTransactionStatus.DELETE_FAIL.ordinal());
			throw new DAOException(e);
		}
		return transactionStatus;
	}

	/**
	 * Method to get all active record instance
	 * 
	 * @param basePojo
	 * @return List of Persisted Instances
	 * @throws DAOException
	 */
	@Override
	public List findAllActive(final BasePojo basePojo) throws DAOException
	{
		try
		{
			((BasePojo) basePojo).setStatus(EnumDBRecordStatus.ACTIVE.status());
			return getHibernateTemplate().executeFind(new HibernateCallback<List<BasePojo>>()
			{
				@Override
				public List<BasePojo> doInHibernate(final Session session) throws HibernateException,
						SQLException
				{
					@SuppressWarnings("unchecked")
					final List<BasePojo> results = (List<BasePojo>) session.createCriteria(
							basePojo.getClass())
							.add(create(basePojo))
							.list();
					log.debug("findAll successful, result size: " + results.size());
					return results;
				}
			});
		}
		catch (Exception e)
		{
			log.error("findAllActive failed", e);
			throw new DAOException(e);
		}
	}

	@Override
	public List<BasePojo> findAllActiveByCriteria(final BasePojo instance,
			final Map<String, Object> mapCriteria) throws DAOException
	{
		try
		{
			((BasePojo) instance).setStatus(EnumDBRecordStatus.ACTIVE.status());
			return getHibernateTemplate().executeFind(new HibernateCallback<List<BasePojo>>()
			{
				@Override
				public List<BasePojo> doInHibernate(final Session session) throws HibernateException,
						SQLException
				{
					final Criteria criteria = session.createCriteria(instance.getClass());
					for (final String columnName : mapCriteria.keySet())
					{
						final Object value = mapCriteria.get(columnName);
						criteria.add(Expression.eq(columnName, value));
					}
					criteria.add(Expression.eq("status", EnumDBRecordStatus.ACTIVE.status()));
					final List<BasePojo> results = criteria.list();
					log.debug("findAllActiveByCriteria successful, result size: " + results.size());
					return results;
				}
			});
		}
		catch (Exception e)
		{
			log.error("findAllActiveByCrieria failed", e);
			throw new DAOException(e);
		}
	}

	@Override
	public Blob getBlob(File file) throws DAOException
	{
		try
		{
			InputStream fis = new FileInputStream(file);
			Session session = getHibernateTemplate().getSessionFactory().openSession();
			return session.getLobHelper().createBlob(fis, fis.available());
		}
		catch (Exception e)
		{
			log.error("java.sql.Blob Conversion Failed", e);
			throw new DAOException(e);
		}
	}
}
